/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.diagram.sequence.ui.composites;

import java.util.Iterator;
import java.util.List;

import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.uml2.uml.Expression;
import org.eclipse.uml2.uml.InstanceSpecification;
import org.eclipse.uml2.uml.InstanceValue;
import org.eclipse.uml2.uml.LiteralBoolean;
import org.eclipse.uml2.uml.LiteralInteger;
import org.eclipse.uml2.uml.LiteralNull;
import org.eclipse.uml2.uml.LiteralString;
import org.eclipse.uml2.uml.LiteralUnlimitedNatural;
import org.eclipse.uml2.uml.Message;
import org.eclipse.uml2.uml.UMLFactory;
import org.eclipse.uml2.uml.ValueSpecification;

import com.cea.papyrus.core.PapyrusPlugin;
import com.cea.papyrus.core.commands.MoveInListCommand;
import com.cea.papyrus.core.editor.CommandStackUtils;
import com.cea.papyrus.core.editor.ModelManager;
import com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil;
import com.cea.papyrus.diagram.sequence.ui.dialogs.MessageArgumentInstanceValueTreeSelectionDialog;
import com.cea.papyrus.diagram.sequence.ui.validators.BooleanInputValidator;
import com.cea.papyrus.diagram.sequence.ui.validators.IntegerInputValidator;
import com.cea.papyrus.diagram.sequence.ui.validators.UMLLabelProvider;
import com.cea.papyrus.diagram.sequence.ui.validators.UnlimitedNaturalInputValidator;
import com.cea.papyrus.tabbedproperties.composites.DecoratedTableComposite;
import com.cea.papyrus.tabbedproperties.composites.ISectionComposite;
import com.cea.papyrus.umlutils.Element;

public class MessageArgumentComposite extends DecoratedTableComposite implements ISectionComposite {

	/**
	 * The Constructor.
	 */
	public MessageArgumentComposite() {
		super("Argument");
	}

	/**
	 * The Constant KEY.
	 */
	private final static String KEY = "argument_data";
	
	/**
	 * The uml classifier.
	 */
	private Message message;
	
	protected ITreeContentProvider contentProvider;
	protected LabelProvider labelProvider;

	/**
	 * The Menu to show all kinds of possible Behavior.
	 */
	private Menu menu;

	/**
	 * Various menu items.
	 */
	private MenuItem 
	expressionItem,
	instanceValueItem,
	literalBooleanItem,
	literalIntegerItem,
	literalStringItem,
	literalUnlimitedNaturalItem,
	literalNullItem;

	/**
	 * Associated selection listeners.
	 */
	private SelectionListener
	expressionItemListener,
	instanceValueItemListener,
	literalBooleanItemListener,
	literalIntegerItemListener,
	literalStringItemListener,
	literalUnlimitedNaturalItemListener,
	literalNullItemListener;
	
	/**
	 * Sets the element.
	 * 
	 * @param element the element
	 */
	@Override
	public void setElement(Element element) {
		super.setElement(element);
		
		//add an additional menu to the add Button
		createMenuForAddButton();
		
		this.message = (Message) element.getUml2Element();
	}

	/**
	 * Adds an additional menu to the addButton. So user can choose which type of trigger to create.
	 */
	private void createMenuForAddButton(){
		menu = new Menu (addButton);
		
		// add Expression menu item
		expressionItem = new MenuItem (menu, SWT.PUSH);
		expressionItem.setText("Expression");
		expressionItem.setImage(PapyrusPlugin.UML_LABEL_PROVIDER.getImage(Expression.class, null));
		expressionItem.addSelectionListener(
				expressionItemListener = 
					new AddElementSelectionListener(Expression.class));

		// add InstanceValue menu item
		instanceValueItem = new MenuItem (menu, SWT.PUSH);
		instanceValueItem.setText("InstanceValue");
		instanceValueItem.setImage(PapyrusPlugin.UML_LABEL_PROVIDER.getImage(InstanceValue.class, null));
		instanceValueItem.addSelectionListener(
				instanceValueItemListener = 
					new AddElementSelectionListener(InstanceValue.class));
		
		// add LiteralBoolean menu item
		literalBooleanItem = new MenuItem (menu, SWT.PUSH);
		literalBooleanItem.setText("LiteralBoolean");
		literalBooleanItem.setImage(PapyrusPlugin.UML_LABEL_PROVIDER.getImage(LiteralBoolean.class, null));
		literalBooleanItem.addSelectionListener(
				literalBooleanItemListener = 
					new AddElementSelectionListener(LiteralBoolean.class));
		
		// add LiteralInteger menu item
		literalIntegerItem = new MenuItem (menu, SWT.PUSH);
		literalIntegerItem.setText("LiteralInteger");
		literalIntegerItem.setImage(PapyrusPlugin.UML_LABEL_PROVIDER.getImage(LiteralInteger.class, null));
		literalIntegerItem.addSelectionListener(
				literalIntegerItemListener = 
					new AddElementSelectionListener(LiteralInteger.class));
		
		// add LiteralString menu item
		literalStringItem = new MenuItem (menu, SWT.PUSH);
		literalStringItem.setText("LiteralString");
		literalStringItem.setImage(PapyrusPlugin.UML_LABEL_PROVIDER.getImage(LiteralString.class, null));
		literalStringItem.addSelectionListener(
				literalStringItemListener = 
					new AddElementSelectionListener(LiteralString.class));
		
		// add LiteralUnlimitedNatural menu item
		literalUnlimitedNaturalItem = new MenuItem (menu, SWT.PUSH);
		literalUnlimitedNaturalItem.setText("LiteralUnlimitedNatural");
		literalUnlimitedNaturalItem.setImage(PapyrusPlugin.UML_LABEL_PROVIDER.getImage(LiteralUnlimitedNatural.class, null));
		literalUnlimitedNaturalItem.addSelectionListener(
				literalUnlimitedNaturalItemListener = 
					new AddElementSelectionListener(LiteralUnlimitedNatural.class));
		
		// add LiteralNull menu item
		literalNullItem = new MenuItem (menu, SWT.PUSH);
		literalNullItem.setText("LiteralNull");
		literalNullItem.setImage(PapyrusPlugin.UML_LABEL_PROVIDER.getImage(LiteralNull.class, null));
		literalNullItem.addSelectionListener(
				literalNullItemListener = 
					new AddElementSelectionListener(LiteralNull.class));

		addButton.setMenu (menu);
	}
	
	/**
	 * Refresh.
	 */
	@Override
	public void refresh() {
		if( !table.isDisposed() 
				&& (message != null)) {
			// Remove use cases from table 
			table.removeAll();

			// Read and add use cases
			Iterator<ValueSpecification> it = message.getArguments().iterator();
			while (it.hasNext()) {
				ValueSpecification value = it.next();
				TableItem item = new TableItem(table, SWT.BORDER);
				item.setImage(labelProvider.getImage(value));
				item.setData(KEY, value);
				item.setText(labelProvider.getText(value));
			}
		}
	}
	
	/**
	 * Adds the button pressed.
	 */
	@Override
	public void addButtonPressed() {
		if(!menu.isDisposed())
			menu.setVisible(true);
	}

	/**
	 * Down button pressed.
	 */
	@Override
	public void downButtonPressed() {
		CompoundCommand compoundCommand = new CompoundCommand();
		TableItem[] tableItems = table.getSelection();

		for(int i = (tableItems.length-1); i >= 0; i--) {
			// Get use case list
			List<ValueSpecification> values = message.getArguments();
			// Get use case
			ValueSpecification value = (ValueSpecification) tableItems[i].getData(KEY);
			int newIndex = table.indexOf(tableItems[i]) + 1;
			if(newIndex < values.size()) {
				// Move
				compoundCommand.add(new MoveInListCommand(values, value, newIndex));
			}
		}
		CommandStack stack = CommandStackUtils.getCommandStack();
		assert stack != null : 
			"Impossible to adapt current editor into a CommandStack";
		stack.execute(compoundCommand);
	}

	/**
	 * Edits the item.
	 * 
	 * @param item the item
	 */
	@Override
	public void editItem(TableItem item) {
	}

	/**
	 * Removes the button pressed.
	 */
	@Override
	public void removeButtonPressed() {
		// Get selection and remove it
		TableItem[] tableItems = table.getSelection();
		for(int i = 0; i < tableItems.length ; i++) {
			// Get use case remove umlClassifier from its subject list 
			// And remove it from the use case list
			ValueSpecification value = (ValueSpecification) tableItems[i].getData(KEY);
			message.getArguments().remove(value);	
		}
	}

	/**
	 * Up button pressed.
	 */
	@Override
	public void upButtonPressed() {
		CompoundCommand compoundCommand = new CompoundCommand();
		TableItem[] tableItems = table.getSelection();
		// Get use case list
		List<ValueSpecification> values = message.getArguments();
		// Get use case
		for(int i = 0; i < tableItems.length ; i++) {
			// get use case
			ValueSpecification value = (ValueSpecification) tableItems[i].getData(KEY);
			int newIndex = table.indexOf(tableItems[i]) - 1;
			if (newIndex >= 0) {
				compoundCommand.add(new MoveInListCommand(values, value, newIndex));
			}
		}
		CommandStack stack = CommandStackUtils.getCommandStack();
		assert stack != null : 
			"Impossible to adapt current editor into a CommandStack";
		stack.execute(compoundCommand);
	}
	
	/**
	 * Disposes of the composite
	 */
	public void dispose(){
		super.dispose();
		
		if ((expressionItem != null) && !expressionItem.isDisposed())
			expressionItem.removeSelectionListener(expressionItemListener);
		if ((instanceValueItem != null) && !instanceValueItem.isDisposed())
			instanceValueItem.removeSelectionListener(instanceValueItemListener);
		if ((literalBooleanItem != null) && !literalBooleanItem.isDisposed())
			literalBooleanItem.removeSelectionListener(literalBooleanItemListener);
		if ((literalIntegerItem != null) && !literalIntegerItem.isDisposed())
			literalIntegerItem.removeSelectionListener(literalIntegerItemListener);
		if ((literalStringItem != null) && !literalStringItem.isDisposed())
			literalStringItem.removeSelectionListener(literalStringItemListener);
		if ((literalUnlimitedNaturalItem != null) && !literalUnlimitedNaturalItem.isDisposed())
			literalUnlimitedNaturalItem.removeSelectionListener(literalUnlimitedNaturalItemListener);
		if ((literalNullItem != null) && !literalNullItem.isDisposed())
			literalNullItem.removeSelectionListener(literalNullItemListener);

	}
	
	/**
	 * Listener for the menu items.
	 */
	public class AddElementSelectionListener implements SelectionListener {
		
		/**
		 * Constructor with given class.
		 * 
		 * @param elementClass Class
		 */
		public AddElementSelectionListener(Class<?> elementClass) {
			this.elementClass = elementClass;
		}

		/**
		 * Stored Class.
		 */
		public Class<?> elementClass;

		/**
		 * Does nothing.
		 * 
		 * @param e SelectionEvent.
		 */
		public void widgetDefaultSelected(SelectionEvent e) {
		}

		/**
		 * What to do when item selected depending on the type of Behavior we want to associate a BehavioredClassifier to.
		 * 
		 * @param e SelectionEvent.
		 */
		public void widgetSelected(SelectionEvent e) {
			org.eclipse.uml2.uml.Element elt = element.getUml2Element();
			
			assert elt != null : 
				"Passed argument is null";
			assert elt instanceof Message : 
				"Passed argument is not a Message";

			int result=42;
			
			Dialog dialog = null;
			Message message = ((Message) elt);
				
			// propose existing behaviors
			if(elementClass.equals(InstanceValue.class)){
				dialog = 
					new MessageArgumentInstanceValueTreeSelectionDialog(
							composite.getShell(), 
							labelProvider,
							contentProvider,
							message);
				((MessageArgumentInstanceValueTreeSelectionDialog) 
						dialog).setInput(element.getUml2Element().getModel());				
				result = dialog.open();
				
			} else if (elementClass.equals(Expression.class)) {
				dialog = 
					new InputDialog(
							composite.getShell(), 
							"Expression", 
							"Enter expression symbol (String) :", 
							null, null);
				result = dialog.open();
				
			} else if (elementClass.equals(LiteralNull.class)) {
				// Nothing to ask
				result = IDialogConstants.OK_ID;
			
			} else if (elementClass.equals(LiteralString.class)) {
				dialog = 
					new InputDialog(
							composite.getShell(), 
							"LiteralString", 
							"Enter string value :", 
							null, null);
				result = dialog.open();
			
			} else if (elementClass.equals(LiteralInteger.class)) {
				dialog = 
					new InputDialog(
							composite.getShell(), 
							"LiteralInteger", 
							"Enter integer value :", 
							"1", 
							new IntegerInputValidator());
				result = dialog.open();
				
			} else if (elementClass.equals(LiteralUnlimitedNatural.class)) {
				dialog = 
					new InputDialog(
							composite.getShell(), 
							"LiteralUnlimitedNatural", 
							"Enter unlimited natural value (0, 1, 2, ..., *) :", 
							null, 
							new UnlimitedNaturalInputValidator());
				result = dialog.open();
			
			} else if (elementClass.equals(LiteralBoolean.class)) {
				dialog = 
					new InputDialog(
							composite.getShell(), 
							"LiteralBoolean", 
							"Enter boolean value (true | false) :", 
							"true", 
							new BooleanInputValidator());
				result = dialog.open();
			}
			
			// result treatment
			if(result==IDialogConstants.OK_ID){
				ValueSpecification value = null;
				if (elementClass.equals(InstanceValue.class)) {
					InstanceSpecification instance = 
						(InstanceSpecification)
						((MessageArgumentInstanceValueTreeSelectionDialog) dialog).getFirstResult();
					
					// Create InstanceValue
					value = UMLFactory.eINSTANCE.createInstanceValue();
					value.setName(ModelUtil.createUniqueName(message.getNamespace(), "InstanceValue_"));
					((InstanceValue) value).setInstance(instance);		
				
				} else if (elementClass.equals(Expression.class)) {
					String symbol = ((InputDialog) dialog).getValue();
					
					// Create Expression
					value = UMLFactory.eINSTANCE.createExpression();
					((Expression) value).setSymbol(symbol);
					value.setName(ModelUtil.createUniqueName(message.getNamespace(), "Expression_"));
					
				} else if (elementClass.equals(LiteralNull.class)) {
										
					// Create LiteralNull
					value = UMLFactory.eINSTANCE.createLiteralNull();
					value.setName(ModelUtil.createUniqueName(message.getNamespace(), "LiteralNull_"));
				
				} else if (elementClass.equals(LiteralString.class)) {
					String inputString = ((InputDialog) dialog).getValue();
					
					// Create LiteralString
					value = UMLFactory.eINSTANCE.createLiteralString();
					((LiteralString) value).setValue(inputString);
					value.setName(ModelUtil.createUniqueName(message.getNamespace(), "LiteralString_"));
					
				} else if (elementClass.equals(LiteralInteger.class)) {
					String inputString = ((InputDialog) dialog).getValue();
					Integer inputInteger = Integer.parseInt(inputString);
					
					// Create LiteralInteger
					value = UMLFactory.eINSTANCE.createLiteralInteger();
					((LiteralInteger) value).setValue(inputInteger);
					value.setName(ModelUtil.createUniqueName(message.getNamespace(), "LiteralInteger_"));
					
				}  else if (elementClass.equals(LiteralUnlimitedNatural.class)) {
					String inputString = ((InputDialog) dialog).getValue();
					Integer inputInteger;
					if (inputString.equals("*")) {
						inputInteger = -1;
					} else {
						inputInteger = Integer.parseInt(inputString);
					}
					
					// Create LiteralUnlimitedNatural
					value = UMLFactory.eINSTANCE.createLiteralUnlimitedNatural();
					((LiteralUnlimitedNatural) value).setValue(inputInteger);
					value.setName(ModelUtil.createUniqueName(message.getNamespace(), "LiteralUnlimitedNatural_"));
					
				}  else if (elementClass.equals(LiteralBoolean.class)) {
					String inputString = ((InputDialog) dialog).getValue();
					Boolean inputBoolean;
					if (inputString.equals("true")) {
						inputBoolean = true;
					} else {
						inputBoolean = false;
					}
					
					// Create LiteralBoolean
					value = UMLFactory.eINSTANCE.createLiteralBoolean();
					((LiteralBoolean) value).setValue(inputBoolean);
					value.setName(ModelUtil.createUniqueName(message.getNamespace(), "LiteralBoolean_"));
					
				}					 
				 
								
				if(value != null) {
					message.getArguments().add(value);
				}
				
				refresh();
			}
		}
	}

	public void setContentProvider(ModelManager modelManager) {
		this.contentProvider = 
			new AdapterFactoryContentProvider(modelManager.getAdapterFactory());
	}
	
	public void setLabelProvider(ModelManager modelManager) {
		this.labelProvider = new UMLLabelProvider(modelManager.getLabelProvider());
	}
}